package com.dnjn.mes.biz.assist;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dnjn.mes.biz.Const;
import com.dnjn.mes.biz.assist.ext.Wrapperz;
import com.dnjn.mes.biz.domain.*;
import com.dnjn.mes.biz.enums.*;
import com.dnjn.mes.biz.modbus.ModbusMasterHelper;
import com.dnjn.mes.biz.service.*;
import com.dnjn.mes.biz.service.helper.WorkShiftHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;


@Slf4j
@Component("recordTask")
public class RecordTaskHandler {

    public static final int SLAVE_ID = 1;
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    public static final DateTimeFormatter DATETIME_FORMATTER_001 = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
    public static final DateTimeFormatter DATETIME_FORMATTER_002 = DateTimeFormatter.ofPattern("yyyyMMddHH");

    @Autowired
    private IMesEquipmentService equipmentService;

    @Autowired
    private IMesWorkpieceService workpieceService;

    @Autowired
    private ModbusMasterHelper modbusMasterHelper;
    @Autowired
    private IMesRecordStatusService recordStatusService;

    @Autowired
    private IMesRecordBeatService beatService;
    @Autowired
    private IMesRecordCapacityService recordCapacityService;
    @Autowired
    private IMesRealtimeCapacityService realtimeCapacityService;
    @Autowired
    private IMesRecordExceptService recordExceptService;
    @Autowired
    private IMesRegisterAddressService registerAddressService;
    @Autowired
    private IMesExceptService exceptService;

    @Autowired
    private IMesMessageService messageService;
    @Autowired
    private WorkShiftHelper workShiftHelper;
    @Autowired
    private IMesWorkforceShiftService shiftService;

    private static String formatDate(LocalDateTime dateTime) {
        return dateTime.format(DATETIME_FORMATTER_001);
    }

    private static String formatDateHour(LocalDateTime dateTime) {
        return dateTime.format(DATETIME_FORMATTER_002);
    }

    /**
     * todo quartz job
     */
    public void handleCapacity(/*Integer addrModbus, DataType dataType*/) {
        log.info("handleCapacity执行啦==================");
        MesEquipment equipment = equipmentService.fetchCurrent();
        MesRegisterAddress address = registerAddressService.fetchAddressByAddrCode(AddrCode.CAPACITY, equipment.getEquipmentId());

        Number number = readModbusHoldingRegister(address.getAddrModbus(), address.getDataType());
        if (Objects.isNull(number)) {
            return;
        }
        Integer currentCapacity = number.intValue();
        LocalDateTime now = LocalDateTime.now();
        refreshRecordCapacity(currentCapacity, now, address.getDataType());
    }

    public void refreshRecordCapacity(Integer currentCapacity, LocalDateTime now, DataType dataType) {
        MesEquipment mesEquipment = equipmentService.getOne(Wrapperz.lambdaQuery(MesEquipment.class).eq(MesEquipment::getIsDefault, BoolType.YES));
        Long equipmentId = mesEquipment.getEquipmentId();
        MesRealtimeCapacity realtimeCapacity = new MesRealtimeCapacity()
                .setCurrentCapacity(currentCapacity)
                .setCurrentDatetime(now)
                .setEquipmentId(equipmentId)
                .setDataType(dataType.getCode());
        realtimeCapacityService.save(realtimeCapacity);

        //todo 过整点第一次 以小时这单位写入recordCapacity
        MesRecordCapacity lastCapacity = recordCapacityService.lastCapacityRecord(equipmentId);
        if (Objects.isNull(lastCapacity)) {
            lastCapacity = new MesRecordCapacity()
                    .setCurrentDatetime(now)
                    .setCurDate(now.toLocalDate())
                    .setCurrentHour(now.getHour())
                    .setStartValue(currentCapacity);
            //lastCapacity.setCreateTime(now);
        }
        LocalDateTime startTime = lastCapacity.getCurDate().atTime(lastCapacity.getCurrentHour(), 0);
        log.info("start: {} cur_dh {} {} ", formatDate(startTime), formatDate(now), currentCapacity);

        if (currentCapacity != 0) { // 更新最近一条记录 ; currentCapacity==0 时 视为换班时间,可能有产量 丢失
            lastCapacity.setEndValue(currentCapacity)
                    .setCapacity(currentCapacity - lastCapacity.getStartValue())
                    .setEquipmentId(equipmentId)
                    .setCurrentDatetime(now);
            updateShiftCapacity(currentCapacity, equipmentId);
        }
        recordCapacityService.saveOrUpdate(lastCapacity);
        if (Duration.between(startTime, now).compareTo(Duration.ofHours(1)) >= 0) {
            //超过一小时 生成新记录
            MesRecordCapacity newRecordCapacity = new MesRecordCapacity()
                    .setEquipmentId(equipmentId)
                    .setStartValue(currentCapacity)
                    .setCurDate(now.toLocalDate())
                    .setCurrentHour(now.getHour())
                    .setCurrentDateHour(formatDateHour(now))//!
                    .setCurrentDatetime(now);
            recordCapacityService.save(newRecordCapacity);
        }

    }

    private void updateShiftCapacity(Integer currentCapacity, Long equipmentId) {
        //获取当前班次和当前时间年月日
        try {
            LocalDate currentDate = LocalDateTime.now().minusHours(8).toLocalDate();
            MesWorkforceConfig current = workShiftHelper.current().getCurrent();
            if (null != current) {
                MesWorkforceShift shift = shiftService.lambdaQuery().eq(MesWorkforceShift::getWorkforceId, current.getWorkforceId()).eq(MesWorkforceShift::getCurrentDate, currentDate)
                        .eq(MesWorkforceShift::getEquipmentId, equipmentId).one();
                int hour = current.getStartTime().getHour();
                //获取当前班次开始时间得设备产能
                /*MesRecordCapacity rc = recordCapacityService.lambdaQuery().eq(MesRecordCapacity::getEquipmentId, equipmentId).eq(MesRecordCapacity::getCurDate, currentDate)
                        .eq(MesRecordCapacity::getCurrentHour, hour).one();
                Integer startValue = 0;
                if (null != rc){
                    startValue = rc.getStartValue();
                }*/
                if (null != shift) {// 0 10  15 5 26
//                    shift.setCapacity(currentCapacity - startValue);
                    shift.setCapacity(currentCapacity);
                    shiftService.saveOrUpdate(shift);
                }
            }
        } catch (Exception e) {
            log.error("插入shift表,产能数据失败");
        }

    }


    /**
     * todo quartz job
     */
    public void handleBeat(/*Integer addrModbus, DataType dataType*/) {
        log.info("handleBeat执行啦==================");
//        MesEquipment mesEquipment = equipmentService.getOne(Wrapperz.lambdaQuery(MesEquipment.class).eq(MesEquipment::getDeleted, "0").eq(MesEquipment::getIsDefault, BoolType.YES));
        MesEquipment equipment = equipmentService.fetchCurrent();
        MesRegisterAddress address = registerAddressService.fetchAddressByAddrCode(AddrCode.BEAT, equipment.getEquipmentId());

        Number number = readModbusHoldingRegister(address.getAddrModbus(), address.getDataType());
        if (Objects.isNull(number)) {
            return;
        }
        MesRecordBeat beat = new MesRecordBeat()
                .setCurrentDatetime(LocalDateTime.now())
                .setDataType(address.getDataType().getCode())
                .setEquipmentId(equipment.getEquipmentId())
                .setDataValue(number.intValue());
        beatService.save(beat);
    }

    public void handleExcept() {
        MesEquipment mesEquipment = equipmentService.getOne(Wrapperz.lambdaQuery(MesEquipment.class).eq(MesEquipment::getIsDefault, BoolType.YES));
        Long equipmentId = mesEquipment.getEquipmentId();
        //todo
        //List<MesExcept> list = exceptService.list();
        List<MesExcept> list = exceptService.list(
                Wrappers.lambdaQuery(MesExcept.class).ne(MesExcept::getDeleted, Const.YES));

        /*Map<Integer, List<MesExcept>> map = list.stream().collect(
                Collectors.groupingBy(MesExcept::getAddrModbus));

        for (Map.Entry<Integer, List<MesExcept>> entry : map.entrySet()) {
            Integer addrModbus = entry.getKey();
            List<MesExcept> alist = entry.getValue();
            MesExcept addr = alist.get(0);
            Number number = readModbusHoldingRegister(addrModbus, addr.getDataType());
            LocalDateTime now = LocalDateTime.now();
            if (null != number && number.intValue() != 0) {
                for (MesExcept address : alist) {
                    //todo
                }

            }
        }*/


        list.forEach(po -> {
            Number number = readModbusHoldingRegister(po.getAddrModbus(), po.getDataType());
            if (null != number && number.intValue() != 0) {
                QueryWrapper<MesRecordExcept> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("except_id", po.getExceptId());
                queryWrapper.eq("except_status", "TODO");
                long count = recordExceptService.count(queryWrapper);
                if (count == 0) {
                    MesRecordExcept rv = new MesRecordExcept()
                            .setCurrentDatetime(LocalDateTime.now())
                            .setDataType(po.getDataType())
                            .setDataValue(number.intValue())
                            .setExceptStatus(ExceptStatus.TODO)
                            .setEquipmentId(equipmentId)
                            .setExceptId(po.getExceptId());
                    recordExceptService.save(rv);
                    MesMessage msg = new MesMessage()
                            .setMessageStatus(Const.YES)
                            .setMessageType(MessageType.WORKPIECE_EXCEPT)
                            .setMessage(po.getExceptMessage())
                            .setMessageCn(po.getExceptMessageCn())
                            .setStartDatetime(LocalDateTime.now())
                            .setEquipmentId(equipmentId)
                            .setExceptId(rv.getExceptId())//? todo
                            .setWorkpieceId(null);
                    messageService.save(msg);
                }

            }
        });
    }

    public void handleStatus() {
        MesEquipment equipment = equipmentService.fetchCurrent();
        List<MesRegisterAddress> list = registerAddressService.fetchAddressByAddrType(AddrType.STATUS);

        log.info("list size {}", list.size());
        for (MesRegisterAddress address : list) {
            Number number = readModbusHoldingRegister(address.getAddrModbus(), address.getDataType());
            log.info("status: {} {}", address.getAddrCode(), number);
            LocalDateTime now = LocalDateTime.now();
            if (null != number && number.intValue() != 0) {
                String status = address.getAddrCode().getStatus();
                MesRecordStatus lastStatusRecord = recordStatusService.lastRecordStatus(status);
                if (Objects.isNull(lastStatusRecord)) {// first thing first
                    recordStatusService.addStatus(status, equipment.getEquipmentId(), now);
                } else {
                    lastStatusRecord.setCurrentDatetime(now);

                    Duration duration = Duration.between(lastStatusRecord.getStartDatetime(), now);
                    BigDecimal durationHour = BigDecimal.valueOf(duration.toMinutes() / 60);

                    if (StringUtils.equals(lastStatusRecord.getStatus(), status)) {//同状态时
                        if (Duration.ofHours(1).compareTo(duration) <= 0) {//同状态运行超过1小时,加一条记录?!
                            updateStatusData(equipment, address, now, status, lastStatusRecord, durationHour,
                                    address.getAddrCode());
                        }
                    } else {
                        updateStatusData(equipment, address, now, status, lastStatusRecord, durationHour,
                                address.getAddrCode());
                    }

                }
            }
        }
    }

    private void updateStatusData(MesEquipment equipment, MesRegisterAddress address, LocalDateTime now, String status,
                                  MesRecordStatus lastStatusRecord, BigDecimal durationHour,
                                  AddrCode addrCode) {
        lastStatusRecord.setEndDatetime(now);
        recordStatusService.updateById(lastStatusRecord);// 最近一条记录收尾

        recordStatusService.addStatus(status, equipment.getEquipmentId(), now);// 新加一条记录

        equipment.updateDuration(address.getAddrCode(), durationHour); //记录状态时长
        equipmentService.updateById(equipment);

        if (AddrCode.RUNNING.equals(addrCode)) {//保养数据更新
            //todo update workpiece  timespent maintainType
            workpieceService.updateTimeSpentByMaintainType(durationHour.doubleValue(), MaintainType.RUNNING_TIME);

        }
    }

    /**
     * todo  addrModbus
     */
    protected Number readModbusHoldingRegister(Integer addrModbus, DataType dataType) {
        try {
            if (Objects.isNull(addrModbus)) {
                log.warn("address.addrModbus.invalid");
                return null;
            }
            return modbusMasterHelper.readHoldingRegister(SLAVE_ID, addrModbus, dataType);
        } catch (Exception e) {
            log.error("[ERROR] " + e.getMessage(), e);
        }
        return null;
    }

}
